#include "stdafx.h"
#include "CPaint.h"
#include "CWindowBuffer.h"
#include "CImage.h"

using namespace Gdiplus;

CPaint::CPaint(HDC hdc)
	: m_graphics(hdc), m_bColorMatrix(false)
{
	m_graphics.SetPageUnit(UnitPixel);
	m_graphics.SetTextRenderingHint(TextRenderingHintAntiAliasGridFit);
	m_graphics.SetPixelOffsetMode(PixelOffsetModeNone);
}

CPaint::~CPaint()
{

}
void CPaint::SetClip(float_32 fX, float_32 fY, float_32 fWidth, float_32 fHeight)
{
	AffineXY(fX, fY);
	m_graphics.SetClip(GpsRectF(fX, fY, fWidth, fHeight));
}

void CPaint::SetTransform(const CMatrix3Float32 & matrix)
{
	Matrix mtr(matrix(0, 0), matrix(0, 1), matrix(1, 0), matrix(1, 1), matrix(2, 0), matrix(2, 1));
	m_graphics.SetTransform(&mtr);
}

void CPaint::GetTransform(CMatrix3Float32 & matrix) const
{
	Matrix mtr;
	m_graphics.GetTransform(&mtr);
	float_32 values[6];
	mtr.GetElements(values);
	matrix.Set(values[0], values[1], values[2], values[3], values[4], values[5]);
}

void CPaint::ResetTransform()
{
	m_graphics.ResetTransform();
}

void CPaint::SetImageColorMatrix(const CMatrix5Float32 & matrix)
{
	m_matrColorMatrix = matrix;
	m_bColorMatrix = true;
}
void CPaint::GetImageColorMatrix(CMatrix5Float32 & matrix) const
{
	matrix = m_matrColorMatrix;
}
void CPaint::ResetImageColorMatrix()
{
	m_bColorMatrix = false;
}


void CPaint::Clear(uint_32 uiColor)
{
	m_graphics.Clear(uiColor);
}

void CPaint::SetPixel(float_32 fX, float_32 fY, uint_32 uiColor)
{
	AffineXY(fX, fY);
	HDC hdc = m_graphics.GetHDC();
	::SetPixel(hdc, Round(fX), Round(fY), uiColor);
	m_graphics.ReleaseHDC(hdc);
}

uint_32 CPaint::GetPixel(float_32 fX, float_32 fY)
{
	AffineXY(fX, fY);
	HDC hdc = m_graphics.GetHDC();
	uint_32 uiColor = ::GetPixel(hdc, Round(fX), Round(fY));
	m_graphics.ReleaseHDC(hdc);
	return uiColor;
}

void CPaint::DrawLine(float_32 fX1, float_32 fY1, float_32 fX2, float_32 fY2, IPen * pPen)
{
	AffineXY(fX1, fY1);
	AffineXY(fX2, fY2);
	m_graphics.DrawLine(&GpsPenEx(pPen), fX1, fY1, fX2, fY2);
}

void CPaint::DrawRect(float_32 fX, float_32 fY, float_32 fWidth, float_32 fHeight, IPen * pPen) 
{
	AffineXY(fX, fY);
	m_graphics.DrawRectangle(&GpsPenEx(pPen), fX, fY, fWidth, fHeight);
}

void CPaint::FillRect(float_32 fX, float_32 fY, float_32 fWidth, float_32 fHeight, IBrush * pBrush)
{
	AffineXY(fX, fY);
	m_graphics.FillRectangle(&GpsSolidBrushEx(pBrush), fX, fY, fWidth, fHeight);
}


void CPaint::DrawBezier3(float_32 fX1, float_32 fY1, float_32 fX2, float_32 fY2, float_32 fX3, float_32 fY3, IPen * pPen)
{
	float_32 fdX = (fX3 - fX1) * 0.5f;
	float_32 fdY = (fY3 - fY1) * 0.5f;
	m_graphics.DrawBezier(&GpsPenEx(pPen), fX1, fY1, fX2 - fdX, fY2 - fdY, fX2 + fdX, fY2 + fdY, fX3, fY3);
}

void CPaint::DrawBezier4(float_32 fX1, float_32 fY1, float_32 fX2, float_32 fY2, 
	float_32 fX3, float_32 fY3, float_32 fX4, float_32 fY4, IPen * pPen)
{
	m_graphics.DrawBezier(&GpsPenEx(pPen), fX1, fY1, fX2, fY2, fX3, fY3, fX4, fY4);
}

void CPaint::DrawArc(float_32 fX, float_32 fY, float_32 fWidth, float_32 fHeight, float_32 fStartAngle, float_32 fAngle, IPen * pPen)
{
	m_graphics.DrawArc(&GpsPenEx(pPen), fX, fY, fWidth, fHeight, fStartAngle, fAngle);
}

void CPaint::DrawString(const char_16 * szText, int_32 iLen, float_32 fX, float_32 fY, IBrush * pBrush)
{
	AffineXY(fX, fY);
	if(iLen < 0)
		iLen = TextLength(szText);
	GpsFontEx font(m_textFormat);
	m_graphics.DrawString(szText, iLen,
		&font,
		GpsPointF(fX, fY),
		NULL, &GpsSolidBrushEx(pBrush));
}

void CPaint::DrawString(const char_16 * szText, int_32 iLen, 
	float_32 fX, float_32 fY, float_32 fWidth, float_32 fHeight, IBrush * pBrush)
{
	AffineXY(fX, fY);
	if(iLen < 0)
		iLen = TextLength(szText);
	GpsFontEx font(m_textFormat);
	m_graphics.DrawString(szText, iLen,
		&font,
		GpsRectF(fX, fY, fWidth, fHeight),
		NULL, &GpsSolidBrushEx(pBrush));
}

void CPaint::DrawImage(IImage * pImage, 
	float_32 fDstX, float_32 fDstY, float_32 fDstWidth, float_32 fDstHeight,
	float_32 fSrcX, float_32 fSrcY, float_32 fSrcWidth, float_32 fSrcHeight)
{
	AffineXY(fDstX, fDstY);
	if(m_bColorMatrix)
	{
		GpsImageAttributes ia;
		ia.SetColorMatrices((GpsColorMatrix *)m_matrColorMatrix.values, nullptr);
		m_graphics.DrawImage(GetImage(pImage),
			GpsRectF(fDstX, fDstY, fDstWidth, fDstHeight),
			fSrcX, fSrcY, fSrcWidth, fSrcHeight, UnitPixel,
			&ia, NULL, NULL);
	}
	else
	{
		m_graphics.DrawImage(GetImage(pImage),
			GpsRectF(fDstX, fDstY, fDstWidth, fDstHeight),
			fSrcX, fSrcY, fSrcWidth, fSrcHeight, UnitPixel,
			NULL, NULL, NULL);
	}
}

void CPaint::SetTextRendLevel(TextRenderlevel renderLevel)
{
	switch(renderLevel)
	{
	case TextRenderLevelSystem:
		m_graphics.SetTextRenderingHint(TextRenderingHintSystemDefault);
		break;
	case TextRenderGray:
		m_graphics.SetTextRenderingHint(TextRenderingHintSingleBitPerPixel);
		break;
	case TextRenderGrayGrid:
		m_graphics.SetTextRenderingHint(TextRenderingHintSingleBitPerPixelGridFit);
		break;
	case TextRenderAntiGray:
		m_graphics.SetTextRenderingHint(TextRenderingHintAntiAlias);
		break;
	case TextRenderAntiGrayGrid:
		m_graphics.SetTextRenderingHint(TextRenderingHintAntiAliasGridFit);
		break;
	case TextRenderClearTypeGrid:
		m_graphics.SetTextRenderingHint(TextRenderingHintClearTypeGridFit);
		break;
	default:
		throw CNotSupportException();
	}
}

void CPaint::SetTextFormat(const FontFormat & format)
{
	m_textFormat = format;
}

void CPaint::GetTextFormat(FontFormat & format) const
{

}

TextRenderlevel CPaint::GetTextRendLevel() const
{
	return (TextRenderlevel)m_graphics.GetTextRenderingHint();
}


void CPaint::MeasureString(const char_16 * szText, int_32 iLen, const FontFormat & format, CSizeInt32 & siMeasure)
{
	VerifyPointer(szText);

	if(iLen < 0)
		iLen = TextLength(szText);

	if(!iLen)
		siMeasure.SetEmpty();
	else
	{
		GpsRectF rect;
		GpsCharacterRange range(0, iLen);
		GpsFontEx font(format);
		GpsStringFormat fmt;
		//GpsRegion region;
		//fmt.SetMeasurableCharacterRanges(1, &range);
		m_graphics.MeasureString(szText, iLen, &font, GpsRectF(0, 0, 0xFFFFFF, 0xFFFFFF), &rect);
		//m_graphics.MeasureCharacterRanges(szText, iLen, &font,  GpsRectF(0, 0, 0xFFFFFF, 0xFFFFFF), &fmt,  1, &region);
		//region.GetBounds(&rect, &m_graphics);
		siMeasure.Width = RoundUp(rect.Width + rect.X);
		siMeasure.Height = RoundUp(rect.Height + rect.Y);
	}
}

void CPaint::DrawPath(const IPath * pPath, IPen * pPen)
{
	PathCommand cmd = PathCommandInvalid;
	int_32 iIndex = 0;
	float_32 fLastX = 0, fLastY = 0;
	float_32 fX = 0, fY = 0;
	float_32 fX1 = 0, fY1 = 0;
	float_32 fX2 = 0, fY2 = 0;
	float_32 fX3 = 0, fY3 = 0;
	bool bWorking = false;

	pPath->BeginIter(iIndex);
	while(pPath->DoIter(iIndex, cmd, fX, fY))
	{
		switch(cmd)
		{
		case PathCommandMoveTo:
			UI_ASSERT(!bWorking);
			bWorking = true;
			break;
		case PathCommandLineTo:
			DrawLine(fLastX, fLastY, fX, fY, pPen);
			break;
		case PathCommandBezierTo3:
			pPath->DoIter(iIndex, cmd, fX1, fY1);
			DrawBezier3(fLastX, fLastY, fX, fY, fX1, fY1, pPen);
			break;
		case PathCommandBezierTo4:
			pPath->DoIter(iIndex, cmd, fX1, fY1);
			pPath->DoIter(iIndex, cmd, fX2, fY2);
			DrawBezier4(fLastX, fLastY, fX, fY, fX1, fY1, fX2, fY2, pPen);
			break;
		case PathCommandArcTo4:
			pPath->DoIter(iIndex, cmd, fX1, fY1);
			pPath->DoIter(iIndex, cmd, fX2, fY2);
			pPath->DoIter(iIndex, cmd, fX3, fY3);
			DrawArc(fX1, fY1, fX2, fY2, fX3, fY3, pPen);
			break;
		default:
			throw CNotSupportException();
		}

		fLastX = fX;
		fLastY = fY;
	}
	pPath->EndIter(iIndex);
}


GpsImage * CPaint::GetImage(IImage * pImage)
{
	VerifyPointer(pImage);
	return ((CImage *)pImage)->GetGpsImage();
}